home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Mail
/
pine3.92
/
pico
/
basic.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-03-14
|
21KB
|
871 lines
#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: basic.c,v 4.25 1996/03/15 07:41:11 hubert Exp $";
#endif
/*
* Program: Cursor manipulation functions
*
*
* Michael Seibel
* Networks and Distributed Computing
* Computing and Communications
* University of Washington
* Administration Builiding, AG-44
* Seattle, Washington, 98195, USA
* Internet: mikes@cac.washington.edu
*
* Please address all bugs and comments to "pine-bugs@cac.washington.edu"
*
*
* Pine and Pico are registered trademarks of the University of Washington.
* No commercial use of these trademarks may be made without prior written
* permission of the University of Washington.
*
* Pine, Pico, and Pilot software and its included text are Copyright
* 1989-1996 by the University of Washington.
*
* The full text of our legal notices is contained in the file called
* CPYRIGHT, included with this distribution.
*
*/
/*
* The routines in this file move the cursor around on the screen. They
* compute a new value for the cursor, then adjust ".". The display code
* always updates the cursor location, so only moves between lines, or
* functions that adjust the top line in the window and invalidate the
* framing, are hard.
*/
#include <stdio.h>
#include <ctype.h>
#include "osdep.h"
#include "pico.h"
#include "estruct.h"
#include "edef.h"
#include "efunc.h"
#ifdef ANSI
int getgoal(struct LINE *);
#else
int getgoal();
#endif
/*
* Move the cursor to the
* beginning of the current line.
* Trivial.
*/
gotobol(f, n)
int f, n;
{
curwp->w_doto = 0;
return (TRUE);
}
/*
* Move the cursor backwards by "n" characters. If "n" is less than zero call
* "forwchar" to actually do the move. Otherwise compute the new cursor
* location. Error if you try and move out of the buffer. Set the flag if the
* line pointer for dot changes.
*/
backchar(f, n)
int f;
register int n;
{
register LINE *lp;
if (n < 0)
return (forwchar(f, -n));
while (n--) {
if (curwp->w_doto == 0) {
if ((lp=lback(curwp->w_dotp)) == curbp->b_linep){
if(Pmaster && Pmaster->headents)
/*
* go up into editing the mail header if on
* the top line and the user hits the left arrow!!!
*
* if the editor returns anything except -1, the
* user requested something special, so let
* pico know...
*/
return(HeaderEditor(2, 1));
else
return (FALSE);
}
curwp->w_dotp = lp;
curwp->w_doto = llength(lp);
curwp->w_flag |= WFMOVE;
} else
curwp->w_doto--;
}
return (TRUE);
}
/*
* Move the cursor to the end of the current line. Trivial. No errors.
*/
gotoeol(f, n)
int f, n;
{
curwp->w_doto = llength(curwp->w_dotp);
return (TRUE);
}
/*
* Move the cursor forwwards by "n" characters. If "n" is less than zero call
* "backchar" to actually do the move. Otherwise compute the new cursor
* location, and move ".". Error if you try and move off the end of the
* buffer. Set the flag if the line pointer for dot changes.
*/
forwchar(f, n)
int f;
register int n;
{
if (n < 0)
return (backchar(f, -n));
while (n--) {
if (curwp->w_doto == llength(curwp->w_dotp)) {
if (curwp->w_dotp == curbp->b_linep)
return (FALSE);
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
}
else
curwp->w_doto++;
}
return (TRUE);
}
/*
* move to a particular line.
* argument (n) must be a positive integer for
* this to actually do anything
*/
gotoline(f, n)
int f, n;
{
if (n < 1) /* if a bogus argument...then leave */
return(FALSE);
/* first, we go to the start of the buffer */
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
return(forwline(f, n-1));
}
/*
* Goto the beginning of the buffer. Massive adjustment of dot. This is
* considered to be hard motion; it really isn't if the original value of dot
* is the same as the new value of dot. Normally bound to "M-<".
*/
gotobob(f, n)
int f, n;
{
curwp->w_dotp = lforw(curbp->b_linep);
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move to the end of the buffer. Dot is always put at the end of the file
* (ZJ). The standard screen code does most of the hard parts of update.
* Bound to "M->".
*/
gotoeob(f, n)
int f, n;
{
curwp->w_dotp = curbp->b_linep;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
return (TRUE);
}
/*
* Move forward by full lines. If the number of lines to move is less than
* zero, call the backward line function to actually do it. The last command
* controls how the goal column is set. Bound to "C-N". No errors are
* possible.
*/
forwline(f, n)
int f, n;
{
register LINE *dlp;
if (n < 0)
return (backline(f, -n));
if ((lastflag&CFCPCN) == 0) /* Reset goal if last */
curgoal = getccol(FALSE); /* not C-P or C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && dlp!=curbp->b_linep)
dlp = lforw(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* This function is like "forwline", but goes backwards. The scheme is exactly
* the same. Check for arguments that are less than zero and call your
* alternate. Figure out the new line and call "movedot" to perform the
* motion. No errors are possible. Bound to "C-P".
*/
backline(f, n)
int f, n;
{
register LINE *dlp;
register int status = 0;
if (n < 0)
return (forwline(f, -n));
if(Pmaster && Pmaster->headents){
/*
* go up into editing the mail header if on the top line
* and the user hits the up arrow!!!
*/
if (lback(curwp->w_dotp) == curbp->b_linep)
/*
* if the editor returns anything except -1 then the user
* has requested something special, so let pico know...
*/
return(HeaderEditor(1, 1));
}
if ((lastflag&CFCPCN) == 0) /* Reset goal if the */
curgoal = getccol(FALSE); /* last isn't C-P, C-N */
thisflag |= CFCPCN;
dlp = curwp->w_dotp;
while (n-- && lback(dlp)!=curbp->b_linep)
dlp = lback(dlp);
curwp->w_dotp = dlp;
curwp->w_doto = getgoal(dlp);
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* go back to the begining of the current paragraph
* here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
* combination to delimit the begining of a paragraph
*/
gotobop(f, n)
int f, n; /* default Flag & Numeric argument */
{
register int txt;
if (n < 0) /* the other way...*/
return(gotoeop(f, -n));
while (n-- > 0) { /* for each one asked for */
/* is there text on the current line? */
for(txt = llength(curwp->w_dotp);
txt > 0 && isspace(lgetc(curwp->w_dotp, txt-1).c);
txt--)
;
if(!txt)
/* scan backward until we do find text */
while(lback(curwp->w_dotp) != curbp->b_linep
&& (!llength(curwp->w_dotp)
|| curwp->w_doto == llength(curwp->w_dotp)
|| isspace(lgetc(curwp->w_dotp, curwp->w_doto).c)))
backchar(FALSE, 1);
/* scan line by line until we come to a line ending with
* a <NL><NL> or <NL><TAB> or <NL><SPACE>
*/
while(lback(curwp->w_dotp) != curbp->b_linep
&& llength(lback(curwp->w_dotp))
&& lgetc(curwp->w_dotp, 0).c != TAB
&& lgetc(curwp->w_dotp, 0).c != ' ')
curwp->w_dotp = lback(curwp->w_dotp);
if(n){
/* keep looking */
if(lback(curwp->w_dotp) == curbp->b_linep)
break;
else
curwp->w_dotp = lback(curwp->w_dotp);
curwp->w_doto = 0;
}
else{
/* leave cursor on first word in para */
curwp->w_doto = 0;
while(isspace(lgetc(curwp->w_dotp, curwp->w_doto).c))
if(++curwp->w_doto >= llength(curwp->w_dotp)){
curwp->w_doto = 0;
curwp->w_dotp = lforw(curwp->w_dotp);
if(curwp->w_dotp == curbp->b_linep)
break;
}
}
}
curwp->w_flag |= WFMOVE; /* force screen update */
return(TRUE);
}
/*
* go forword to the end of the current paragraph
* here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
* combination to delimit the begining of a paragraph
*/
gotoeop(f, n)
int f, n; /* default Flag & Numeric argument */
{
register int txt;
if (n < 0) /* the other way...*/
return(gotobop(f, -n));
while (n-- > 0) { /* for each one asked for */
/* is there text on the current line? */
for(txt = llength(curwp->w_dotp);
txt > 0 && isspace(lgetc(curwp->w_dotp, txt-1).c);
txt--)
;
if(!txt)
/* scan forword until we do find text */
while(curwp->w_dotp != curbp->b_linep
&& (!llength(curwp->w_dotp)
|| curwp->w_doto == llength(curwp->w_dotp)
|| isspace(lgetc(curwp->w_dotp, curwp->w_doto).c)))
forwchar(FALSE, 1);
/* scan line by line until we come to a line ending with
* a <NL><NL> or <NL><TAB> or <NL><SPACE>
*/
while(curwp->w_dotp != curbp->b_linep
&& llength(lforw(curwp->w_dotp))
&& lgetc(lforw(curwp->w_dotp), 0).c != TAB
&& lgetc(lforw(curwp->w_dotp), 0).c != ' ')
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = llength(curwp->w_dotp);
/* still looking? */
if(n){
if(curwp->w_dotp == curbp->b_linep)
break;
else
curwp->w_dotp = lforw(curwp->w_dotp);
curwp->w_doto = 0;
}
}
curwp->w_flag |= WFMOVE; /* force screen update */
return(curwp->w_dotp != curbp->b_linep);
}
/*
* This routine, given a pointer to a LINE, and the current cursor goal
* column, return the best choice for the offset. The offset is returned.
* Used by "C-N" and "C-P".
*/
getgoal(dlp)
register LINE *dlp;
{
register int c;
register int col;
register int newcol;
register int dbo;
col = 0;
dbo = 0;
while (dbo != llength(dlp)) {
c = lgetc(dlp, dbo).c;
newcol = col;
if (c == '\t')
newcol |= 0x07;
else if (c<0x20 || c==0x7F)
++newcol;
++newcol;
if (newcol > curgoal)
break;
col = newcol;
++dbo;
}
return (dbo);
}
/*
* Scroll the display forward (up) n lines.
*/
scrollforw (n, movedot)
register int n;
int movedot;
{
register LINE *lp;
LINE *lp2;
register int nl;
int i;
nl = n;
lp = curwp->w_linep;
while (n-- && lp!=curbp->b_linep)
lp = lforw(lp);
if (movedot) { /* Move dot to top of page. */
curwp->w_dotp = lp;
curwp->w_doto = 0;
}
curwp->w_flag |= WFHARD;
if(lp == curbp->b_linep)
return(TRUE);
else
curwp->w_linep = lp;
/*
* if the header is open, close it ...
*/
if(Pmaster && Pmaster->headents && ComposerTopLine != COMPOSER_TOP_LINE){
n -= ComposerTopLine - COMPOSER_TOP_LINE;
ToggleHeader(0);
}
/*
* scroll down from the top the same number of lines we've moved
* forward
*/
if(optimize)
scrollup(curwp, -1, nl-n-1);
if(!movedot){
/* Requested to not move the dot. Look for the dot in the current
* window. loop through all lines, stop when at end of window
* or endof buffer. If the dot is found, it can stay where it
* is, otherwise we do need to move it.
*/
movedot = TRUE;
for ( lp2 = lp, i = 0;
lp2 != curbp->b_linep && i < curwp->w_ntrows;
lp2 = lforw(lp2), ++i) {
if (curwp->w_dotp == lp2) {
movedot = FALSE;
break;
}
}
if (movedot) {
/* Dot not found in window. Move to first line of window. */
curwp->w_dotp = lp;
curwp->w_doto = 0;
}
}
return (TRUE);
}
/*
* Scroll forward by a specified number of lines, or by a full page if no
* argument. Bound to "C-V". The "2" in the arithmetic on the window size is
* the overlap; this value is the default overlap value in ITS EMACS. Because
* this zaps the top line in the display window, we have to do a hard update.
*/
forwpage(f, n)
int f;
register int n;
{
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Forget the overlap */
n = 1; /* if tiny window. */
} else if (n < 0)
return (backpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
return (scrollforw (n, TRUE));
}
/*
* Scroll back (down) number of lines.
*/
scrollback (n, movedot)
register int n;
int movedot;
{
register LINE *lp, *tp;
register int nl;
int status = 0;
int i;
if(Pmaster && Pmaster->headents){
/*
* go up into editing the mail header if on the top line
* and the user hits the up arrow!!!
*/
if (lback(curwp->w_dotp) == curbp->b_linep){
/*
* if the editor returns anything except -1 then the user
* has requested something special, so let pico know...
*/
return(HeaderEditor(1, 1));
}
}
/*
* Count back the number of lines requested.
*/
nl = n;
lp = curwp->w_linep;
while (n-- && lback(lp)!=curbp->b_linep)
lp = lback(lp);
curwp->w_linep = lp;
curwp->w_flag |= WFHARD;
/*
* scroll down from the top the same number of lines we've moved
* forward
*
* This isn't too cool, but it has to be this way so we can
* gracefully scroll in the message header
*/
if(Pmaster && Pmaster->headents){
if((lback(lp)==curbp->b_linep) && (ComposerTopLine==COMPOSER_TOP_LINE))
n -= entry_line(1000, TRUE); /* never more than 1000 headers */
if(nl-n-1 < curwp->w_ntrows)
if(optimize)
scrolldown(curwp, -1, nl-n-1);
}
else
if(optimize)
scrolldown(curwp, -1, nl-n-1);
if(Pmaster && Pmaster->headents){
/*
* if we're at the top of the page, and the header is closed,
* open it ...
*/
if((lback(lp) == curbp->b_linep)
&& (ComposerTopLine == COMPOSER_TOP_LINE)){
ToggleHeader(1);
movecursor(ComposerTopLine, 0);
}
}
/*
* Decide if we move the dot or not. Calculation done AFTER deciding
* if we display the header because that will change the number of
* lines on the screen.
*/
if (movedot) {
/* Dot gets put at top of window. */
curwp->w_dotp = curwp->w_linep;
curwp->w_doto = 0;
}
else {
/* Requested not to move dot, but we do need to keep in on
* the screen. Verify that it is still in the range of lines
* visable in the window. Loop from the first line to the
* last line, until we reach the end of the buffer or the end
* of the window. If we find the dot, then we don't need
* to move it. */
movedot = TRUE;
for ( tp = curwp->w_linep, i = 0;
tp != curbp->b_linep && i < curwp->w_ntrows;
tp = lforw(tp), ++i) {
if (curwp->w_dotp == tp) {
movedot = FALSE;
break;
}
}
if (movedot) {
/* Dot not found in window. Move to last line of window. */
curwp->w_dotp = lback (tp);
curwp->w_doto = 0;
}
}
return (TRUE);
}
/*
* This command is like "forwpage", but it goes backwards. The "2", like
* above, is the overlap between the two windows. The value is from the ITS
* EMACS manual. Bound to "M-V". We do a hard update for exactly the same
* reason.
*/
backpage(f, n)
int f;
register int n;
{
if (f == FALSE) {
n = curwp->w_ntrows - 2; /* Default scroll. */
if (n <= 0) /* Don't blow up if the */
n = 1; /* window is tiny. */
} else if (n < 0)
return (forwpage(f, -n));
#if CVMVAS
else /* Convert from pages */
n *= curwp->w_ntrows; /* to lines. */
#endif
return (scrollback (n, TRUE));
}
scrollupline (f, n)
int f, n;
{
return (scrollback (1, FALSE));
}
scrolldownline (f, n)
int f, n;
{
return (scrollforw (1, FALSE));
}
/*
* Scroll to a position.
*/
scrollto (f, n)
int f, n;
{
#ifdef _WINDOWS
long scrollLine;
LINE *lp;
int i;
scrollLine = mswin_getscrollto ();
/*
* Starting at the first data line in the buffer, step forward
* 'scrollLine' lines to find the new top line. It is a circular
* list of buffers, so watch for the first line to reappear. if
* it does, we have some sort of internal error, abort scroll
* operation. Also watch for NULL, just in case.
*/
lp = lforw (curbp->b_linep);
for (i = 0; i < scrollLine && lp != curbp->b_linep && lp != NULL; ++i)
lp = lforw(lp);
if (lp == curbp->b_linep || lp == NULL)
return (FALSE); /* Whoops! */
/* Set the new top line for the window and flag a redraw. */
curwp->w_linep = lp;
curwp->w_dotp = lp;
curwp->w_doto = 0;
curwp->w_flag |= WFHARD;
if(Pmaster && Pmaster->headents){
/*
* If we are at the top of the page and header not open, open it.
* If we are not at the top of the page and the header is open,
* close it.
*/
if((lback(lp) == curbp->b_linep)
&& (ComposerTopLine == COMPOSER_TOP_LINE)){
ToggleHeader(1);
movecursor(ComposerTopLine, 0);
}
else if((lback(lp) != curbp->b_linep)
&& (ComposerTopLine != COMPOSER_TOP_LINE)){
ToggleHeader (0);
}
}
return (TRUE);
#endif
}
/*
* Set the mark in the current window to the value of "." in the window. No
* errors are possible. Bound to "M-.". If told to set an already set mark
* unset it.
*/
setmark(f, n)
int f, n;
{
if(!curwp->w_markp){
curwp->w_markp = curwp->w_dotp;
curwp->w_marko = curwp->w_doto;
emlwrite("Mark Set", NULL);
}
else{
/* clear inverse chars between here and dot */
markregion(0);
curwp->w_markp = NULL;
emlwrite("Mark UNset", NULL);
}
#ifdef _WINDOWS
mswin_allowcopycut(curwp->w_markp ? kremove : NULL);
#endif
return (TRUE);
}
/*
* Swap the values of "." and "mark" in the current window. This is pretty
* easy, bacause all of the hard work gets done by the standard routine
* that moves the mark about. The only possible error is "no mark". Bound to
* "C-X C-X".
*/
swapmark(f, n)
int f, n;
{
register LINE *odotp;
register int odoto;
if (curwp->w_markp == NULL) {
if(Pmaster == NULL)
emlwrite("No mark in this window", NULL);
return (FALSE);
}
odotp = curwp->w_dotp;
odoto = curwp->w_doto;
curwp->w_dotp = curwp->w_markp;
curwp->w_doto = curwp->w_marko;
curwp->w_markp = odotp;
curwp->w_marko = odoto;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
/*
* Set the mark in the current window to the value of "." in the window. No
* errors are possible. Bound to "M-.". If told to set an already set mark
* unset it.
*/
setimark(f, n)
int f, n;
{
curwp->w_imarkp = curwp->w_dotp;
curwp->w_imarko = curwp->w_doto;
return(TRUE);
}
/*
* Swap the values of "." and "mark" in the current window. This is pretty
* easy, bacause all of the hard work gets done by the standard routine
* that moves the mark about. The only possible error is "no mark". Bound to
* "C-X C-X".
*/
swapimark(f, n)
int f, n;
{
register LINE *odotp;
register int odoto;
if (curwp->w_imarkp == NULL) {
if(Pmaster == NULL)
emlwrite("Programmer botch! No mark in this window", NULL);
return (FALSE);
}
odotp = curwp->w_dotp;
odoto = curwp->w_doto;
curwp->w_dotp = curwp->w_imarkp;
curwp->w_doto = curwp->w_imarko;
curwp->w_imarkp = odotp;
curwp->w_imarko = odoto;
curwp->w_flag |= WFMOVE;
return (TRUE);
}
#ifdef MOUSE
/*
* Handle a mouse down.
*/
mousepress (f, n)
int f, n;
{
MOUSEPRESS mp;
LINE *lp;
int i;
mouse_get_last (NULL, &mp);
lp = curwp->w_linep;
i = mp.row - ((Pmaster && Pmaster->headents) ? ComposerTopLine : 2);
if (i < 0) {
if (Pmaster) {
/* Clear existing region. */
if (curwp->w_markp)
setmark(0,1);
/* Move to top of document before editing header. */
curwp->w_dotp = curwp->w_linep;
curwp->w_doto = 0;
curwp->w_flag |= WFMOVE;
update (); /* And update. */
return (HeaderEditor (1, 1));
}
}
else {
while(i-- && lp != curbp->b_linep)
lp = lforw(lp);
curgoal = mp.col;
curwp->w_dotp = lp;
curwp->w_doto = getgoal(lp);
curwp->w_flag |= WFMOVE;
if(mp.doubleclick)
setmark(0, 1);
}
}
#endif